<?php
// +----------------------------------------------------------------------
// | 云静Admin
// +----------------------------------------------------------------------
// | Copyright (c) 2019-2020 http://www.yunj.net.cn
// +----------------------------------------------------------------------
// | 云静Admin提供个人非商业用途免费使用。
// +----------------------------------------------------------------------
// | Author: Uncle-L <1732983738@qq.com>
// +----------------------------------------------------------------------
// | 应用的公共函数库
// +----------------------------------------------------------------------

use think\Response;
use think\facade\Request;
use yunj\enum\TipsTemplet;
use yunj\builder\YunjForm;
use yunj\builder\YunjTable;
use yunj\builder\YunjImport;
use think\response\Redirect;
use yunj\Config as YunjConfig;
use think\exception\HttpResponseException;

if (!function_exists('yunj_config')) {
    /**
     * Notes: 基础配置
     * Author: Uncle-L
     * Date: 2020/10/20
     * Time: 14:45
     * @param string $key [配置参数名，第一级为配置文件名。支持多级配置 .号分割。如：yunj_config('errcode.10000')]
     * @param null $default [默认值]
     * @return mixed|null
     */
    function yunj_config(string $key, $default = null) {
        static $config;
        $config = $config ?: [];
        // fileName、keyArr
        if (!strstr($key, '.')) {
            $fileName = $key;
            $keyArr = [];
        } else {
            $keyArr = explode('.', $key);
            $fileName = array_shift($keyArr);
        }

        // fileConfig
        if (!isset($config[$fileName])) {
            $file = YUNJ_PATH . 'config/' . $fileName . '.php';
            if (!file_exists($file)) return $default;
            $config[$fileName] = include($file);
        }
        if (!$keyArr) return $config[$fileName];

        // filterConfig
        $filterConfig = $config[$fileName];
        foreach ($keyArr as $k) {
            if (isset($filterConfig[$k])) {
                $filterConfig = $filterConfig[$k];
            } else {
                return $default;
            }
        }
        return $filterConfig;
    }
}

if (!function_exists('view_template')) {
    /**
     * Notes: 视图模板地址
     * Author: Uncle-L
     * Date: 2021/11/3
     * Time: 16:02
     * @param string $template
     * @return string
     */
    function view_template(string $template): string {
        $path = "../application/yunj/view/";
        $template .= substr($template, -5) !== ".html" ? ".html" : "";
        return $path . $template;
    }
}

if (!function_exists('YF')) {
    /**
     * Notes: 创建一个表单构建器实例
     * Author: Uncle-L
     * Date: 2021/10/9
     * Time: 11:59
     * @param string $id
     * @param array $args
     * @return YunjForm
     */
    function YF(string $id, array $args = []): YunjForm {
        return new YunjForm($id, $args);
    }
}

if (!function_exists('YT')) {
    /**
     * Notes: 创建一个表格构建器实例
     * Author: Uncle-L
     * Date: 2021/10/9
     * Time: 12:00
     * @param string $id
     * @param array $args
     * @return YunjTable
     */
    function YT(string $id, array $args = []): YunjTable {
        return new YunjTable($id, $args);
    }
}

if (!function_exists('YI')) {
    /**
     * Notes: 创建一个导入构建器实例
     * Author: Uncle-L
     * Date: 2021/10/9
     * Time: 12:00
     * @param string $id
     * @param array $args
     * @return YunjImport
     */
    function YI(string $id, array $args = []): YunjImport {
        return new YunjImport($id, $args);
    }
}

/****************************************************** 跳转 ******************************************************/
if (!function_exists('url_tips')) {
    /**
     * Notes: 提示页面地址
     * Author: Uncle-L
     * Date: 2020/10/21
     * Time: 17:54
     * @param int $templet [提示模板]
     * @param string $msg [提示消息]
     * @return string
     */
    function url_tips(int $templet = TipsTemplet::ERROR, string $msg = ''): string {
        $vars = input('get.');
        $vars['templet'] = $templet;
        if ($msg && is_string($msg)) $vars['msg'] = $msg;
        return url(yunj_config("admin.error_controller") . "/tips", $vars);
    }
}

if (!function_exists('redirect_tips')) {
    /**
     * Notes: 重定向到提示页
     * Author: Uncle-L
     * Date: 2020/10/21
     * Time: 18:09
     * @param int $templet [提示模板]
     * @param string $msg [提示消息]
     * @return Redirect
     */
    function redirect_tips(int $templet = TipsTemplet::ERROR, string $msg = ''): Redirect {
        return redirect(url_tips($templet, $msg));
    }
}

if (!function_exists('throw_redirect')) {
    /**
     * Notes: 抛出重定向
     * Author: Uncle-L
     * Date: 2021/10/19
     * Time: 14:53
     * @param string $url
     */
    function throw_redirect(string $url): void {
        // 重定向到当前地址不行
        if (strstr(Request::url(), $url)) return;
        header('content-type:text/html;charset=uft-8');
        header('location:' . $url);
        exit;
    }
}

/****************************************************** 输出 ******************************************************/

if (!function_exists('response_msg')) {
    /**
     * Notes: 返回错误码对应消息
     * Author: Uncle-L
     * Date: 2020/7/20
     * Time: 13:56
     * @param int $errcode
     * @param string $msg
     * @return string
     */
    function response_msg(Int $errcode = 10000, string $msg = '') {
        return $errcode < 90000 ? YunjConfig::get('errcode.' . $errcode, $msg) : yunj_config('errcode.' . $errcode, $msg);
    }
}

if (!function_exists('response_json')) {
    /**
     * Notes: 返回固定格式json数据
     * Author: Uncle-L
     * Date: 2020/7/8
     * Time: 22:19
     * @param int $errcode
     * @param string $msg
     * @param null $data
     * @return \think\response\Json
     */
    function response_json(Int $errcode = 10000, string $msg = '', $data = null) {
        $msg = $msg ?: response_msg($errcode) ?: '';
        $result = ['errcode' => $errcode, 'msg' => $msg, 'data' => $data];
        return json($result);
    }
}

if (!function_exists('success_json')) {
    /**
     * Notes: 返回成功格式的json数据
     * Author: Uncle-L
     * Date: 2020/1/14
     * Time: 17:47
     * @param null $data
     * @return \think\response\Json
     */
    function success_json($data = null) {
        $msg = response_msg(0);
        return response_json(0, $msg, $data);
    }
}

if (!function_exists('error_json')) {
    /**
     * Notes: 返回失败格式的json数据
     * Author: Uncle-L
     * Date: 2020/1/14
     * Time: 17:49
     * @param string $msg
     * @return \think\response\Json
     */
    function error_json(string $msg = '') {
        $msg = $msg ?: response_msg(10000);
        return response_json(10000, $msg);
    }
}

if (!function_exists('throw_json')) {
    /**
     * Notes: 抛出固定格式json数据
     * Author: Uncle-L
     * Date: 2020/7/8
     * Time: 22:24
     * @param int|Response $errcode
     * @param string $msg
     * @param null $data
     */
    function throw_json($errcode = 10000, string $msg = '', $data = null) {
        $response = $errcode instanceof Response ? $errcode : json(['errcode' => $errcode, 'msg' => $msg, 'data' => $data]);
        throw new HttpResponseException($response);
    }
}

if (!function_exists('throw_success_json')) {
    /**
     * Notes: 抛出成功格式的json数据
     * Author: Uncle-L
     * Date: 2020/3/21
     * Time: 18:20
     * @param null $data
     */
    function throw_success_json($data = null) {
        $response = success_json($data);
        throw_json($response);
    }
}

if (!function_exists('throw_error_json')) {
    /**
     * Notes: 抛出错误格式的json数据
     * Author: Uncle-L
     * Date: 2020/3/21
     * Time: 18:21
     * @param string $msg
     */
    function throw_error_json(string $msg = '') {
        $response = error_json($msg);
        throw_json($response);
    }
}

/****************************************************** 数组 ******************************************************/

if (!function_exists('array_eq')) {
    /**
     * Notes: 判断两个数组的长度、键值对是否一一对应且相等
     * Author: Uncle-L
     * Date: 2020/10/26
     * Time: 16:56
     * @param array $arr1
     * @param array $arr2
     * @return bool
     */
    function array_eq(array $arr1 = [], array $arr2 = []) {
        if (count($arr1) != count($arr2)) return false;
        foreach ($arr1 as $k => $v) {
            if (!isset($arr2[$k]) || $arr2[$k] != $v) return false;
        }
        return true;
    }
}

if (!function_exists('array_in')) {
    /**
     * Notes: 判断数组值是否在指定规则数组内（只针对一维数组）
     * Author: Uncle-L
     * Date: 2020/2/17
     * Time: 14:19
     * @param array $arr [待判断数组]
     * @param array $rule_arr [规则数组]
     * @return bool
     */
    function array_in(array $arr = [], array $rule_arr = []) {
        if (!$arr || !$rule_arr || !is_array($arr) || !is_array($rule_arr)) return false;
        if (count($arr) > count($rule_arr)) return false;
        foreach ($arr as $v) {
            if (!in_array($v, $rule_arr)) return false;
        }
        return true;
    }
}

if (!function_exists('array_supp')) {
    /**
     * Notes: 数组补齐
     * Author: Uncle-L
     * Date: 2020/3/3
     * Time: 18:47
     * @param array $arr [待补齐数组]
     * @param array $rule_arr [规则数组]
     * @return array|bool
     */
    function array_supp($arr = [], $rule_arr = []) {
        if (!$arr || !is_array($arr)) return $rule_arr;
        foreach ($rule_arr as $rule_k => $rule_v) {
            if (!isset($arr[$rule_k])) continue;
            $v = $arr[$rule_k];
            if (is_array($rule_v)) $v = array_depth($rule_v, 1) ? ($v + $rule_v) : array_supp($v, $rule_v);
            $rule_arr[$rule_k] = $v;
        }
        return $rule_arr;
    }
}

if (!function_exists('array_depth')) {
    /**
     * Notes: 获取/判断数组维度
     * Author: Uncle-L
     * Date: 2020/6/28
     * Time: 18:04
     * @param array $arr
     * @param int $rule_depth [规则维度，不传则返回数组维度|传入则校验数组是否该维度]
     * @return int|bool
     */
    function array_depth(array $arr = [], int $rule_depth = -1) {
        $arrs = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));
        $depth = 0;
        foreach ($arrs as $v)
            $arrs->getDepth() >= $depth and $depth = $arrs->getDepth();
        return is_positive_integer($rule_depth) ? ++$depth === $rule_depth : ++$depth;
    }
}

if (!function_exists('array_insert')) {
    /**
     * Notes: 在数组指定key前插入数组
     * Author: Uncle-L
     * Date: 2020/7/18
     * Time: 23:46
     * @param array $arr [目标数组]
     * @param array $data [待插入数组]
     * @param bool $key [指定key]
     * @return array
     */
    function array_insert(&$arr = [], $data = [], $key = false) {
        if (!is_array($data)) return $arr;
        $key = $key === false ? key($arr) : $key;
        if (!isset($arr[$key])) return $arr;
        $arrHead = [];
        $arrFoot = [];
        $headSliceEnd = false;
        foreach ($arr as $k => $v) {
            if (!$headSliceEnd) {
                if ($k == $key) {
                    $headSliceEnd = true;
                    $arrFoot[$k] = $v;
                } else {
                    $arrHead[$k] = $v;
                }
            } else {
                $arrFoot[$k] = $v;
            }
        }
        $arr = $arrHead + $data + $arrFoot;
        return $arr;
    }
}

if (!function_exists('array_key_prefix')) {
    /**
     * Notes: 给数组所有key或指定key增加前缀
     * Author: Uncle-L
     * Date: 2021/1/8
     * Time: 16:37
     * @param array $arr
     * @param string $prefix [前缀]
     * @param string $key [指定key，注意：若新key在原数组中存在则前缀翻倍再计算]
     * @return array
     */
    function array_key_prefix(&$arr = [], string $prefix, string $key = '') {
        if (!$arr || !$prefix) return $arr;
        if ($key) {
            if (!array_key_exists($key, $arr)) return $arr;
            $newKey = $prefix . $key;
            if (array_key_exists($newKey, $arr)) return array_key_prefix($arr, $prefix . $prefix, $key);
            $arr[$newKey] = $arr[$key];
            unset($arr[$key]);
            return $arr;
        }
        $arr = array_combine(array_map(function ($key) use ($prefix) {
            return $prefix . $key;
        }, array_keys($arr)), $arr);
        return $arr;
    }
}

/****************************************************** 验证 ******************************************************/

if (!function_exists('is_mobile')) {
    /**
     * Notes: 是否移动端判断
     * Author: Uncle-L
     * Date: 2020/2/19
     * Time: 9:21
     * @return bool [是移动端返回true|反之]
     */
    function is_mobile() {
        $_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTTP'] : '';
        $mobile_browser = '0';
        if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i', strtolower($_SERVER['HTTP_USER_AGENT'])))
            $mobile_browser++;
        if ((isset($_SERVER['HTTP_ACCEPT'])) and (strpos(strtolower($_SERVER['HTTP_ACCEPT']), 'application/vnd.wap.xhtml+xml') !== false))
            $mobile_browser++;
        if (isset($_SERVER['HTTP_X_WAP_PROFILE']))
            $mobile_browser++;
        if (isset($_SERVER['HTTP_PROFILE']))
            $mobile_browser++;
        $mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'], 0, 4));
        $mobile_agents = array(
            'w3c ', 'acs-', 'alav', 'alca', 'amoi', 'audi', 'avan', 'benq', 'bird', 'blac',
            'blaz', 'brew', 'cell', 'cldc', 'cmd-', 'dang', 'doco', 'eric', 'hipt', 'inno',
            'ipaq', 'java', 'jigs', 'kddi', 'keji', 'leno', 'lg-c', 'lg-d', 'lg-g', 'lge-',
            'maui', 'maxo', 'midp', 'mits', 'mmef', 'mobi', 'mot-', 'moto', 'mwbp', 'nec-',
            'newt', 'noki', 'oper', 'palm', 'pana', 'pant', 'phil', 'play', 'port', 'prox',
            'qwap', 'sage', 'sams', 'sany', 'sch-', 'sec-', 'send', 'seri', 'sgh-', 'shar',
            'sie-', 'siem', 'smal', 'smar', 'sony', 'sph-', 'symb', 't-mo', 'teli', 'tim-',
            'tosh', 'tsm-', 'upg1', 'upsi', 'vk-v', 'voda', 'wap-', 'wapa', 'wapi', 'wapp',
            'wapr', 'webc', 'winw', 'winw', 'xda', 'xda-'
        );
        if (in_array($mobile_ua, $mobile_agents))
            $mobile_browser++;
        if (strpos(strtolower($_SERVER['ALL_HTTP']), 'operamini') !== false)
            $mobile_browser++;
        // win
        if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false)
            $mobile_browser = 0;
        // win7
        if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows phone') !== false)
            $mobile_browser++;
        if ($mobile_browser > 0) {
            return true;
        }
        return false;
    }
}

if (!function_exists('is_positive_integer')) {
    /**
     * Notes: 正整数验证
     * Author: Uncle-L
     * Date: 2020/3/21
     * Time: 17:38
     * @param $num
     * @return bool [是正整数返回true|反之]
     */
    function is_positive_integer($num) {
        if (is_numeric($num) && is_int($num + 0) && ($num + 0) > 0) {
            return true;
        }
        return false;
    }
}

if (!function_exists('is_json')) {
    /**
     * Notes: json数据格式验证
     * Author: Uncle-L
     * Date: 2020/3/21
     * Time: 17:38
     * @param $str
     * @return bool [是json数据格式返回true|反之]
     */
    function is_json($str) {
        json_decode($str);
        return (json_last_error() == JSON_ERROR_NONE);
    }
}

if (!function_exists('is_datetime')) {
    /**
     * Notes: 时间日期有效性验证
     * Author: Uncle-L
     * Date: 2020/5/26
     * Time: 16:53
     * @param $datetime [待校验的时间日期]
     * @param string $format [需要校验的时间日期格式]
     * @return bool
     */
    function is_datetime($datetime, $format = 'Y-m-d H:i:s') {
        $timestamp = strtotime($datetime);
        if (!$timestamp) return false;
        return date($format, $timestamp) == $datetime;
    }
}

if (!function_exists('is_md5_result')) {
    /**
     * Notes: 判断是否为md5加密结果
     * Author: Uncle-L
     * Date: 2021/7/2
     * Time: 17:19
     * @param string $result
     * @param int $len
     * @return bool
     */
    function is_md5_result(string $result, int $len = 32): bool {
        if (!$result || strlen($result) != $len) return false;
        return !!preg_match("/^[a-z0-9]{32}$/", $result);
    }
}

/****************************************************** 时间日期 ******************************************************/

if (!function_exists('msectime')) {
    /**
     * Notes: 获取当前毫秒时间戳
     * Author: Uncle-L
     * Date: 2020/11/23
     * Time: 11:35
     * @return bool|string
     */
    function msectime() {
        list($msec, $sec) = explode(' ', microtime());
        $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
        return substr($msectime, 0, 13);
    }
}

/****************************************************** 字符串 ******************************************************/

if (!function_exists('rand_char')) {
    /**
     * Notes: 获取指定长度随机字符串
     * Author: Uncle-L
     * Date: 2020/11/23
     * Time: 11:38
     * @param int $len [默认长度32]
     * @return null|string
     */
    function rand_char($len = 32) {
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol) - 1;
        for ($i = 0; $i < $len; $i++) {
            $str .= $strPol[rand(0, $max)];
        }
        return $str;
    }
}

if (!function_exists('filter_sql')) {
    /**
     * Notes: 过滤sql，防止sql注入
     * Author: Uncle-L
     * Date: 2021/1/5
     * Time: 18:27
     * @param string $str
     * @return string
     */
    function filter_sql(string $str) {
        if (!is_string($str) || !$str) return '';
        $str = addslashes($str);
        $str = str_replace("_", "\_", $str);
        $str = str_replace("%", "\%", $str);
        $str = str_replace(" ", "", $str);
        $str = nl2br($str); // 回车转换
        $str = htmlspecialchars($str);
        return $str;
    }
}

if (!function_exists('start_with')) {
    /**
     * Notes: 检测字符串是否以指定的字符串开始/指定位置开始
     * Author: Uncle-L
     * Date: 2021/2/4
     * Time: 10:27
     * @param string $str [要检查的字符串]
     * @param string $pattern [指定字符串]
     * @param int $idx [开始位置]
     * @return bool
     */
    function start_with(string $str, string $pattern, int $idx = 0) {
        return strpos($str, $pattern) === $idx;
    }
}

if (!function_exists('uppercase_to_underline')) {
    /**
     * Notes: 首字母大写的驼峰格式转换为下划线分割
     * Author: Uncle-L
     * Date: 2021/4/15
     * Time: 14:04
     * @param string $str
     * @return string
     */
    function uppercase_to_underline(string $str) {
        return strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $str));
    }
}

if (!function_exists('underline_to_uppercase')) {
    /**
     * Notes: 下划线转换为首字母大写的驼峰格式
     * Author: Uncle-L
     * Date: 2021/4/15
     * Time: 14:04
     * @param string $str
     * @return string
     */
    function underline_to_uppercase(string $str) {
        $arr = explode('_', $str);
        $str = '';
        foreach ($arr as $v) {
            $str .= ucfirst($v);
        }
        return $str;
    }
}

if (!function_exists('calling_class')) {
    /**
     * Notes: 获取调用此方法的类名
     * Author: Uncle-L
     * Date: 2021/4/15
     * Time: 14:15
     * @return mixed
     */
    function calling_class() {
        $trace = debug_backtrace();
        $class = $trace[1]['class'];

        for ($i = 1; $i < count($trace); $i++) {
            if (isset($trace[$i]))
                if ($class != $trace[$i]['class'])
                    return $trace[$i]['class'];
        }
    }
}

if (!function_exists('fuzzy_mobile_number')) {
    /**
     * Notes: 模糊手机号码（num=<7直接输出，7<num<11前后各保留2位，num>=11前后各保留3、4位，其余字符用*代替）
     * Author: Uncle-L
     * Date: 2021/4/28
     * Time: 11:38
     * @param $num
     * @return string
     */
    function fuzzy_mobile_number($num) {
        $len = strlen($num);
        if ($len <= 7) return $num;
        list($beforeDigit, $afterDigit) = $len > 7 && $len < 11 ? [2, 2] : [3, 4];
        $newNum = substr($num, 0, $beforeDigit)
            . str_repeat("*", $len - ($beforeDigit + $afterDigit))
            . substr($num, $len - ($afterDigit + 1), $afterDigit);
        return $newNum;
    }
}

if (!function_exists('exception_to_str')) {
    /**
     * Notes: Exception异常对象转换为字符串显示
     * Author: Uncle-L
     * Date: 2021/10/18
     * Time: 16:11
     * @param Exception $e
     * @return string
     */
    function exception_to_str(\Exception $e): string {
        if (!$e) return "";
        $str = get_class($e) . " " . $e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine() . "\r\n"
            . "Stack trace:\r\n" . $e->getTraceAsString();
        return $str;
    }
}